﻿Imports NAudio.Wave
Imports Q2Q.Audio.Outputs
Imports Q2Q.Audio.Devices
Imports Q2Q.UI
Namespace Audio.Players
	Public Class WasapiPlayer
		Inherits PlayerBase
		Implements IDisposable

#Region "Properties"

		Private WithEvents WasapiOutputProperty As NAudio.Wave.WasapiOut 'Audio.Outputs.WasapiOut
		Private Property WasapiOutput As NAudio.Wave.WasapiOut 'Audio.Outputs.WasapiOut
			Get
				Return WasapiOutputProperty
			End Get
			Set(value As NAudio.Wave.WasapiOut)
				WasapiOutputProperty = value
				RaisePropertyChanged("WasapiOutput")
			End Set
		End Property

		Private SilenceProperty As SilenceWaveProvider
		Private Property Silence As SilenceWaveProvider
			Get
				Return SilenceProperty
			End Get
			Set(value As SilenceWaveProvider)
				SilenceProperty = value
				RaisePropertyChanged("Silence")
			End Set
		End Property

		Private MultiplexProperty As MultiplexingWaveProvider
		Private Property Multiplex As MultiplexingWaveProvider
			Get
				Return MultiplexProperty
			End Get
			Set(value As MultiplexingWaveProvider)
				MultiplexProperty = value
				RaisePropertyChanged("Multiplex")
			End Set
		End Property


		''' <summary>
		''' Represents the WaveController Property.
		''' </summary>
		''' <remarks></remarks>
		Private WaveControllerProperty As WaveChannel32
		''' <summary>
		''' Controls the various aspects of the playback, such as pan and volume.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Property WaveController As WaveChannel32
			Get
				Return WaveControllerProperty
			End Get
			Set(value As WaveChannel32)
				WaveControllerProperty = value
				RaisePropertyChanged("WaveController")
				RaisePropertyChanged("ActualVolume")
			End Set
		End Property

		''' <summary>
		''' Returns a value that indicates the current volume of the WaveController. Returns 0 if nothing has been initialized.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public ReadOnly Property ActualVolume As Single
			Get
				If WaveController IsNot Nothing Then
					Return WaveController.Volume
				Else
					Return 0
				End If
			End Get
		End Property


		''' <summary>
		''' Represents the Reader Property.
		''' </summary>
		''' <remarks></remarks>
		Private WithEvents ReaderProperty As LoopingAudioFileReader = Nothing	'MediaFoundationReader = Nothing
		''' <summary>
		''' The WaveStream that reads the file in for playback.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Property Reader As LoopingAudioFileReader	'MediaFoundationReader
			Get
				Return ReaderProperty
			End Get
			Set(value As LoopingAudioFileReader)
				ReaderProperty = value
				RaisePropertyChanged("Reader")
			End Set
		End Property

		''' <summary>
		''' Gets a readonly value of whether or not the audio is playing.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overrides ReadOnly Property IsPlaying As Boolean
			Get
				If WasapiOutput IsNot Nothing Then
					Return WasapiOutput IsNot Nothing AndAlso WasapiOutput.PlaybackState = PlaybackState.Playing
				Else
					Return False
				End If
			End Get
		End Property

		''' <summary>
		''' Gets a readonly value of whether or not the audio is stopped.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overrides ReadOnly Property IsStopped As Boolean
			Get
				If WasapiOutput IsNot Nothing Then
					Return WasapiOutput IsNot Nothing AndAlso WasapiOutput.PlaybackState = PlaybackState.Stopped
				Else
					Return True
				End If
			End Get
		End Property

		''' <summary>
		''' Gets a read only value of whether the audio is paused.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overrides ReadOnly Property IsPaused As Boolean
			Get
				If WasapiOutput IsNot Nothing Then
					Return WasapiOutput IsNot Nothing AndAlso WasapiOutput.PlaybackState = PlaybackState.Paused
				Else
					Return False
				End If
			End Get
		End Property

		''' <summary>
		''' The name of the file that was previously played. If this is different from the file currently trying to be played (because the audio cue had the file changed) then we recreate the playback stream.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Property PreviousFile As String = ""

		''' <summary>
		''' Sets the current volume of the cue.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Overrides Property Volume As Single
			Get
				Return VolumeProperty
			End Get
			Set(value As Single)
				If value > 1 Then
					value = 1
				ElseIf value < 0 Then
					value = 0
				End If
				VolumeProperty = value
				RaisePropertyChanged("Volume")
				If WaveController IsNot Nothing Then
					WaveController.Volume = value * My.Settings.MasterVolume
					RaisePropertyChanged("ActualVolume")
				End If
			End Set
		End Property

		''' <summary>
		''' Represents the Volume Property.
		''' </summary>
		''' <remarks></remarks>
		Private VolumeProperty As Single = 0.5

		''' <summary>
		''' Gets or Sets the pan value for the player. -1 is hard left. +1 is hard right.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overrides Property Pan As Single
			Get
				Return PanProperty
			End Get
			Set(value As Single)
				If value > 1 Then
					value = 1
				ElseIf value < -1 Then
					value = -1
				End If
				PanProperty = value
				RaisePropertyChanged("Pan")
				If WaveController IsNot Nothing Then
					WaveController.Pan = value
				End If
			End Set
		End Property

		''' <summary>
		''' Represents the Pan Property.
		''' </summary>
		''' <remarks></remarks>
		Private PanProperty As Single = 0

		''' <summary>
		''' Gets or Sets whether the audio file should loop on playback.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overrides Property EnableLoop As Boolean
			Get
				Return EnableLoopProperty
			End Get
			Set(value As Boolean)
				EnableLoopProperty = value
				If Reader IsNot Nothing Then
					Reader.EnableLooping = value
				End If
				RaisePropertyChanged("Loop")
			End Set
		End Property

		''' <summary>
		''' Represents the Loop Property.
		''' </summary>
		''' <remarks></remarks>
		Private EnableLoopProperty As Boolean = True

		''' <summary>
		''' When set to true will prevent the audio file from continuing to loop past the point it is 'stopped'.
		''' </summary>
		''' <remarks></remarks>
		Private PreventLoop As Boolean = False

		Private ChannelsListProperty As List(Of Q2Q.Audio.Devices.DeviceChannel) = Nothing
		Public Property ChannelsList As List(Of Q2Q.Audio.Devices.DeviceChannel)
			Get
				Return ChannelsListProperty
			End Get
			Set(value As List(Of Q2Q.Audio.Devices.DeviceChannel))
				ChannelsListProperty = value
				RaisePropertyChanged("ChannelsList")
			End Set
		End Property



#End Region

#Region "Public Events"
		''' <summary>
		''' Raised when the WaveOutEvent assumes the Stopped state.
		''' </summary>
		''' <remarks></remarks>
		Public Shadows Event PlaybackStopped(sender As Object, e As RoutedEventArgs)

		''' <summary>
		''' Occurs when the current time is updated.
		''' </summary>
		Public Shadows Event TimeUpdated(sender As Object, e As Helpers.TimeUpdatedEventArgs)

#End Region

#Region "Internal Functions"

		''' <summary>
		''' Raised when the WasapiOutput stops after the audio has finished playing or an error occurs.
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Private Sub WasapiOutput_PlaybackStopped(sender As Object, e As StoppedEventArgs) Handles WasapiOutputProperty.PlaybackStopped
			'If e.Exception IsNot Nothing Then
			'	My.Application.Show.Console.SendMessage(e.Exception.Message)
			'End If
			PreventLoop = False
			RaisePropertyChanged("IsPlaying")
			RaisePropertyChanged("IsStopped")
			RaiseEvent PlaybackStopped(Me, New RoutedEventArgs())
			If My.Application IsNot Nothing Then
				My.Application.Show.Console.SendMessage("Audio cue playback was stopped.", e.Exception)
			End If
		End Sub

		''' <summary>
		''' Handles the DoRead event of the Reader control.
		''' </summary>
		''' <param name="sender">The source of the event.</param>
		''' <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
		Private Sub Reader_DoRead(sender As Object, e As EventArgs) Handles ReaderProperty.DoRead
			Dim tim As TimeSpan = Reader.TotalTime - Reader.CurrentTime
			If tim.TotalSeconds <= FadeOutDuration AndAlso Not EnableLoop Then
				If Not IsFadingOut Then
					FadeOut()
				End If
			End If
			RaiseEvent TimeUpdated(Me, New Helpers.TimeUpdatedEventArgs(tim.Hours.ToString("D2") & ":" & tim.Minutes.ToString("D2") & ":" & tim.Seconds.ToString("D2")))
		End Sub

		''' <summary>
		''' Updates volumes based on the master volume.
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Private Sub ProcessVolumeChange(sender As Object, e As EventArgs)
			If WaveController IsNot Nothing Then
				WaveController.Volume = TargetVolume * My.Settings.MasterVolume
			End If
		End Sub

#End Region

#Region "Public Functions"

		''' <summary>
		''' Creates a new audio Player. Requires the name of the file to play.
		''' </summary>
		''' <param name="FileToPlay"></param>
		''' <remarks></remarks>
		Public Sub New(FileToPlay As String)
			Try
				File = FileToPlay
				AddHandler CType(My.Application.MainWindow, DesignWindow).MasterVolumeChanged, AddressOf ProcessVolumeChange
			Catch
			End Try
		End Sub

		''' <summary>
		''' Starts the file playing or resumes the player from pause.
		''' </summary>
		''' <remarks></remarks>
		Public Overrides Sub Play(Optional UseFade As Boolean = True)
			If File Is Nothing OrElse File = "" Then
				My.Application.Show.Console.SendMessage("A Cue Attempted to Play with an Invalid File or URL")
				Return
			End If
			Dim playbackState As PlaybackState = playbackState.Stopped
			If WasapiOutput IsNot Nothing Then
				playbackState = WasapiOutput.PlaybackState
			End If
			If playbackState <> NAudio.Wave.PlaybackState.Paused Then
				If (PreviousFile IsNot Nothing AndAlso PreviousFile <> "" AndAlso PreviousFile <> File) Then
					Reader.Dispose()
					Reader = Nothing
				End If
				If Reader Is Nothing Then
					Try
						'Reader = New LoopingMediaFoundationReader(File)
						Reader = New LoopingAudioFileReader(File)
					Catch ex As Exception
						My.Application.Show.Console.SendMessage("There was an error prepping an audio cue for playback!", ex)
						RaiseEvent PlaybackStopped(Me, New RoutedEventArgs())
						Return
					End Try
					PreviousFile = File
					If EnableLoop Then
						Reader.EnableLooping = True
					End If
					WaveController = New WaveChannel32(Reader, TargetVolume, Pan)
					RaisePropertyChanged("ActualVolume")
					WaveController.PadWithZeroes = False

					Silence = New SilenceWaveProvider(WaveController.WaveFormat)

					'WasapiOutput = New Audio.Outputs.WasapiOut(NAudio.CoreAudioApi.AudioClientShareMode.Shared, False, My.Settings.PlayerLatency)
					WasapiOutput = New NAudio.Wave.WasapiOut(NAudio.CoreAudioApi.AudioClientShareMode.Shared, False, My.Settings.PlayerLatency)
					'The WasapiOutput is a small modification to the original NAudio WasapiOutput class that fixes an issue with playback stopping prematurely.
					'See https://naudio.codeplex.com/workitem/16363


					Multiplex = New MultiplexingWaveProvider({WaveController, Silence}, 8)

					'These variables represent the left and right channels of the audio stream and the silence.
					'If a mono audio file has been provided, then we have to account for there only being two streams
					'in the multiplex provider, instead of three. So we check for the count of channels and then
					'if needbe we set LC and RC to both take the mono audio channel.
					Dim LC As Integer = 0
					Dim RC As Integer = 1
					Dim SC As Integer = 2
					If WaveController.WaveFormat.Channels = 1 Then
						LC = 0
						RC = 0
						SC = 1
					ElseIf WaveController.WaveFormat.Channels = 2 Then
						LC = 0
						RC = 1
						SC = 2
					End If


					If ChannelsList IsNot Nothing Then
						For Each d As DeviceChannel In ChannelsList
							If d.ChannelID = 0 Then
								If d.IsEnabled Then
									If My.Application.Show.ShowFile.Channel01Settings.IsEnabled Then
										Multiplex.ConnectInputToOutput(LC, 0)
										Multiplex.ConnectInputToOutput(RC, 1)
									Else
										Multiplex.ConnectInputToOutput(SC, 0)
										Multiplex.ConnectInputToOutput(SC, 1)
									End If
								Else
									Multiplex.ConnectInputToOutput(SC, 0)
									Multiplex.ConnectInputToOutput(SC, 1)
								End If
							ElseIf d.ChannelID = 1 Then
								If d.IsEnabled Then
									If My.Application.Show.ShowFile.Channel23Settings.IsEnabled Then
										Multiplex.ConnectInputToOutput(LC, 2)
										Multiplex.ConnectInputToOutput(RC, 3)
									Else
										Multiplex.ConnectInputToOutput(SC, 2)
										Multiplex.ConnectInputToOutput(SC, 3)
									End If
								Else
									Multiplex.ConnectInputToOutput(SC, 2)
									Multiplex.ConnectInputToOutput(SC, 3)
								End If
							ElseIf d.ChannelID = 2 Then
								If d.IsEnabled Then
									If My.Application.Show.ShowFile.Channel45Settings.IsEnabled Then
										Multiplex.ConnectInputToOutput(LC, 4)
										Multiplex.ConnectInputToOutput(RC, 5)
									Else
										Multiplex.ConnectInputToOutput(SC, 4)
										Multiplex.ConnectInputToOutput(SC, 5)
									End If
								Else
									Multiplex.ConnectInputToOutput(SC, 4)
									Multiplex.ConnectInputToOutput(SC, 5)
								End If
							ElseIf d.ChannelID = 3 Then
								If d.IsEnabled Then
									If My.Application.Show.ShowFile.Channel67Settings.IsEnabled Then
										Multiplex.ConnectInputToOutput(LC, 6)
										Multiplex.ConnectInputToOutput(RC, 7)
									Else
										Multiplex.ConnectInputToOutput(SC, 6)
										Multiplex.ConnectInputToOutput(SC, 7)
									End If
								Else
									Multiplex.ConnectInputToOutput(SC, 6)
									Multiplex.ConnectInputToOutput(SC, 7)
								End If
							End If
						Next
					Else
						With My.Application.Show.ShowFile
							If (.Channel01Settings.IsEnabled AndAlso .Channel01Settings.IsDefault) Then
								Multiplex.ConnectInputToOutput(LC, 0)
								Multiplex.ConnectInputToOutput(RC, 1)
							ElseIf .Channel01Settings.IsEnabled Then
								Multiplex.ConnectInputToOutput(SC, 0)
								Multiplex.ConnectInputToOutput(SC, 1)
							End If
							If (.Channel23Settings.IsEnabled AndAlso .Channel23Settings.IsDefault) Then
								Multiplex.ConnectInputToOutput(LC, 2)
								Multiplex.ConnectInputToOutput(RC, 3)
							ElseIf .Channel23Settings.IsEnabled Then
								Multiplex.ConnectInputToOutput(SC, 2)
								Multiplex.ConnectInputToOutput(SC, 3)
							End If
							If (.Channel45Settings.IsEnabled AndAlso .Channel45Settings.IsDefault) Then
								Multiplex.ConnectInputToOutput(LC, 4)
								Multiplex.ConnectInputToOutput(RC, 5)
							ElseIf .Channel45Settings.IsEnabled Then
								Multiplex.ConnectInputToOutput(SC, 4)
								Multiplex.ConnectInputToOutput(SC, 5)
							End If
							If (.Channel67Settings.IsEnabled AndAlso .Channel67Settings.IsDefault) Then
								Multiplex.ConnectInputToOutput(LC, 6)
								Multiplex.ConnectInputToOutput(RC, 7)
							ElseIf .Channel67Settings.IsEnabled Then
								Multiplex.ConnectInputToOutput(SC, 6)
								Multiplex.ConnectInputToOutput(SC, 7)
							End If
						End With
					End If


					Try
						If FadeInDuration > 0 AndAlso UseFade Then
							WaveController.Volume = 0
							RaisePropertyChanged("ActualVolume")
							WasapiOutput.Init(Multiplex)
							FadeIn()
						Else
							WaveController.Volume = TargetVolume * My.Settings.MasterVolume
							WasapiOutput.Init(Multiplex)
						End If
					Catch ex As Exception
						My.Application.Show.Console.SendMessage("There was a problem initializing audio playback!", ex)
					End Try
				End If
			End If

			'WaveController.CurrentTime = StartTime
			Reader.StartTime = StartTime

			Try
				WasapiOutput.Play()
				If IsFadingIn AndAlso FadeInTimer IsNot Nothing Then
					FadeInTimer.Start()
				ElseIf IsFadingOut AndAlso FadeOutTimer IsNot Nothing Then
					FadeOutTimer.Start()
				End If
			Catch ex As Exception
				My.Application.Show.Console.SendMessage("There was a problem starting the audio file.", ex)
				If Me.IsStopped Then
					RaiseEvent PlaybackStopped(Me, New RoutedEventArgs())
				End If
				Return
			End Try
			RaisePropertyChanged("IsPlaying")
			RaisePropertyChanged("IsStopped")
			RaisePropertyChanged("IsPaused")
			My.Application.Show.Console.SendMessage("Audio cue playback was started.")
		End Sub

		' ''' <summary>
		' ''' Stops the audio player.
		' ''' </summary>
		' ''' <remarks></remarks>
		Public Overrides Sub [Stop](Optional UseFade As Boolean = True)
			If Not IsStopped Then
				PreventLoop = True
				If WasapiOutput IsNot Nothing Then
					If FadeOutDuration > 0 AndAlso UseFade Then
						FadeOut()
					Else
						WasapiOutput.Stop()
						RaisePropertyChanged("IsPlaying")
						RaisePropertyChanged("IsStopped")
						RaisePropertyChanged("IsPaused")
					End If
				End If
			End If
		End Sub

		''' <summary>
		''' Sets the volume of the cue playback to zero, instantly.
		''' </summary>
		''' <remarks></remarks>
		Public Overrides Sub Mute()
			If WaveController Is Nothing Then Return
			WaveController.Volume = 0
		End Sub

		''' <summary>
		''' Pauses the audio playback.
		''' </summary>
		''' <remarks></remarks>
		Public Overrides Sub Pause()
			If WasapiOutput IsNot Nothing Then
				WasapiOutput.Pause()
				RaisePropertyChanged("IsPlaying")
				RaisePropertyChanged("IsStopped")
				RaisePropertyChanged("IsPaused")
				If IsFadingIn AndAlso FadeInTimer IsNot Nothing Then
					FadeInTimer.Stop()
				ElseIf IsFadingOut AndAlso FadeOutTimer IsNot Nothing Then
					FadeOutTimer.Stop()
				End If
			End If

		End Sub

		''' <summary>
		''' Dispose of the internal pieces of the player when ready to remove it from memory
		''' </summary>
		''' <remarks></remarks>
		Public Overloads Sub Dispose() Implements IDisposable.Dispose
			'If Not IsPlaying Then
			Dispose(True)
			GC.SuppressFinalize(Me)
			'End If
		End Sub

		''' <summary>
		''' Handles the actual disposing of the interal parts of the player.
		''' </summary>
		''' <param name="disposing"></param>
		''' <remarks></remarks>
		Protected Overridable Overloads Sub Dispose(disposing As Boolean)
			RemoveHandler CType(My.Application.MainWindow, DesignWindow).MasterVolumeChanged, AddressOf ProcessVolumeChange
			If disposing Then
				If WaveController IsNot Nothing Then
					Try
						WaveController.Dispose()
					Catch ex As Exception
						My.Application.Show.Console.SendMessage("There was a problem releasing an audio cue's resources. This may cause memory problems.", ex)
					End Try
					WaveController = Nothing
				End If
				If WasapiOutput IsNot Nothing Then
					WasapiOutput.Dispose()
					WasapiOutput = Nothing
					Silence = Nothing
					Multiplex = Nothing
					RaisePropertyChanged("IsPlaying")
					RaisePropertyChanged("IsStopped")
					RaisePropertyChanged("IsPaused")
				End If
				If Reader IsNot Nothing Then
					Reader.Dispose()
					Reader = Nothing
				End If
			End If
		End Sub

#End Region

#Region "Fading"

		''' <summary>
		''' The amount the sound should be stepped by when fading in.
		''' </summary>
		''' <remarks></remarks>
		Private FadeInStep As Double

		''' <summary>
		''' The amount the sound should be stepped by when fading out.
		''' </summary>
		''' <remarks></remarks>
		Private FadeOutStep As Double



		''' <summary>
		''' Fade in the audio after the player is playing.
		''' </summary>
		''' <remarks></remarks>
		Private Sub FadeIn()
			If IsFadingIn Then
				Return
			End If
			FadeInStep = ((TargetVolume * My.Settings.MasterVolume) / FadeInDuration) / 10 '(TargetVolume / FadeInDuration) / 10 '
			FadeInTimer = New Timers.Timer(100)
			AddHandler FadeInTimer.Elapsed, AddressOf DoFadeInStep
			IsFadingIn = True
			FadeInTimer.Start()
		End Sub
		''' <summary>
		''' Private timer used when fading in audio.
		''' </summary>
		''' <remarks></remarks>
		Private FadeInTimer As Timers.Timer

		''' <summary>
		''' Returns True when the player is fading in audio.
		''' </summary>
		''' <remarks></remarks>
		Private IsFadingIn As Boolean = False

		''' <summary>
		''' Called by the timer created in the FadeIn sub to handle raising the volume by the prescribed amount.
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Private Sub DoFadeInStep(sender As Object, e As System.Timers.ElapsedEventArgs)
			If WaveController IsNot Nothing Then
				If Not IsFadingIn Then
					CType(sender, System.Timers.Timer).Stop()
					RemoveHandler CType(sender, System.Timers.Timer).Elapsed, AddressOf DoFadeInStep
					Return
				End If
				Try
					WaveController.Volume += FadeInStep
					RaisePropertyChanged("ActualVolume")
					If WaveController.Volume >= TargetVolume * My.Settings.MasterVolume Then
						CType(sender, System.Timers.Timer).Stop()
						RemoveHandler CType(sender, System.Timers.Timer).Elapsed, AddressOf DoFadeInStep
						FadeInTimer = Nothing
					End If
				Catch
				End Try
			End If
		End Sub


		''' <summary>
		''' Returns true if the player is fading out audio.
		''' </summary>
		Private IsFadingOut As Boolean = False

		''' <summary>
		''' Fade out the audio and then stop the player.
		''' </summary>
		''' <remarks></remarks>
		Private Sub FadeOut()
			If IsFadingOut Then
				Return
			End If
			'Determine the time remaining for the audio cue that's playing back.
			Dim remainder As Integer = 0
			If WaveController.CurrentTime > WaveController.TotalTime Then
				remainder = WaveController.TotalTime.TotalSeconds - (WaveController.CurrentTime.TotalSeconds Mod WaveController.TotalTime.TotalSeconds)
			Else
				remainder = (WaveController.TotalTime - WaveController.CurrentTime).TotalSeconds
			End If

			'If the time remaining is greater than the requested fade out duration then use the fade out duration, otherwise fade out based on the remaining time.
			If FadeOutDuration < remainder Then
				FadeOutStep = (WaveController.Volume / FadeOutDuration) / 10 '((WaveController.Volume * My.Settings.MasterVolume) / FadeOutDuration) / 10
			Else
				FadeOutStep = (WaveController.Volume / remainder) / 10 '((WaveController.Volume * My.Settings.MasterVolume) / remainder) / 10
			End If
			FadeOutTimer = New Timers.Timer(100)
			AddHandler FadeOutTimer.Elapsed, AddressOf DoFadeOutStep
			IsFadingIn = False
			IsFadingOut = True
			FadeOutTimer.Start()
		End Sub
		''' <summary>
		''' Private timer used when fading out audio.
		''' </summary>
		''' <remarks></remarks>
		Private FadeOutTimer As Timers.Timer

		''' <summary>
		''' Called by the timer created in the FadeOut sub to handle lowering the volume by the prescribed amount.
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Private Sub DoFadeOutStep(sender As Object, e As System.Timers.ElapsedEventArgs)
			If WaveController IsNot Nothing Then
				If IsStopped OrElse IsFadingOut = False Then
					CType(sender, System.Timers.Timer).Stop()
					RemoveHandler CType(sender, System.Timers.Timer).Elapsed, AddressOf DoFadeOutStep
					IsFadingOut = False
					FadeOutTimer = Nothing
					Return
				End If
				Try
					WaveController.Volume -= FadeOutStep
					RaisePropertyChanged("ActualVolume")
					If WaveController.Volume <= 0 Then
						IsFadingOut = False
						CType(sender, System.Timers.Timer).Stop()
						RemoveHandler CType(sender, System.Timers.Timer).Elapsed, AddressOf DoFadeOutStep
						FadeOutTimer = Nothing
						If Reader.EnableLooping AndAlso PreventLoop = False Then
							If FadeInDuration > 0 Then
								FadeIn()
							Else
								WaveController.Volume = TargetVolume
								RaisePropertyChanged("ActualVolume")
							End If
						Else
							If WasapiOutput IsNot Nothing Then
								WasapiOutput.Stop()
								RaisePropertyChanged("IsPlaying")
								RaisePropertyChanged("IsStopped")
								RaisePropertyChanged("IsPaused")
							End If
						End If
					End If
				Catch
				End Try
			End If
		End Sub

#End Region

	End Class
End Namespace
